﻿/// <reference path="../typings/index.d.ts" />

import { 配置 } from '../配置/配置'
import *as http from 'http'
import *as dgram from 'dgram'
import *as io from 'socket.io'
import *as redis from 'ioredis'
import *as uuid from 'uuid'
import *as mysql from 'mysql'
import { 加解密 } from './工具/加解密'
import { 登录服务 } from './登录服务'
import {
    系统权限, 登录账号, 注册账号, 注册结果, 回复结果, 办理业务, 开办业务,
    申请私匙, 收回私匙, 修改密码, 找回密码, 修改应用, 修改手机, 注销账号,
    修改邮箱, 注册应用
} from './内置服务/数据类型'

import { Redis数据库服务 } from './内置服务/Redis数据库/Redis数据库服务'
import *as 阿里短信 from 'topSdk'
import *as 邮件验证 from 'nodemailer'
import { 是否为数组 } from './工具/数组判断'
//import *as 日志 from 'log4js'
export class 消息中心 {
    private IO: SocketIO.Server
    private 服务: http.Server
    private 订阅: redis.Redis
    private 发布: redis.Redis
    private 名称空间: SocketIO.Namespace
    private 系统密码: string
    private 全局id: string
    private 本服id: string
    private 系统名称: string
    private 系统开始运行时间: number
    private 登录: 登录服务
    private Redis: Redis数据库服务
    private 短信验证器
    private 短信配置
    private 邮件验证器: 邮件验证.Transporter
    private 邮件验证配置: 邮件模板
    private 邮件验证参数配置
    constructor(服务: http.Server) {
        this.系统开始运行时间 = Date.now()
        let Redis数据库连接 = 配置.Redis数据库配置.连接 || null
        this.Redis = new Redis数据库服务(Redis数据库连接)
        this.本服id = 配置.消息中心配置.基本配置.本服id
        this.服务 = 服务
        this.IO = io.listen(this.服务)
        this.系统名称 = 配置.消息中心配置.基本配置.系统名称 || 'MyMQ消息中心'
        this.系统密码 = 配置.消息中心配置.基本配置.密码 || null
        this.全局id = 配置.消息中心配置.基本配置.全局id || 加解密.不可逆加密(this.系统名称)
        this.短信验证器 = new 阿里短信.ApiClient(配置.消息中心配置.短信验证配置.主机配置)
        this.短信配置 = 配置.消息中心配置.短信验证配置.模板配置
        this.邮件验证器 = 邮件验证.createTransport(配置.消息中心配置.邮件验证配置.主机配置)
        this.邮件验证配置 = new 邮件模板(配置.消息中心配置.邮件验证配置.模板配置)
        this.邮件验证参数配置 = 配置.消息中心配置.邮件验证配置.参数配置
        this.订阅 = new redis(Redis数据库连接)
        this.发布 = new redis(Redis数据库连接)
        this.登录 = new 登录服务(this.Redis, this.订阅, this.发布)
        this.消息循环()
    }
    启动() {
        this.名称空间 = this.IO.use(this.登录.登录)
        this.名称空间.on('connection', async (套接字) => {
            if (套接字['账号'].已登录) {
                套接字.emit('完成登录', null, '成功')
            }
            套接字.on('注册账号', async (数据: 注册账号, 回调: (err, 结果) => void) => {
                try {
                    if (!回调 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    let 电话 = 数据.电话 || 'null'
                    let 邮箱 = 数据.邮箱 || 'null'
                    let 密码 = 数据.密码 || null
                    if (!密码) {
                        回调('密码不能为空', null)
                        return
                    }
                    let 密码验证 = this.注册数据验证({ 电话, 邮箱, 密码 })
                    if (!密码验证) {
                        回调('密码长度6~16个字符,且不能包含非法字符', null)
                        return
                    }
                    let 注册时间 = Date.now().toString()
                    let 账号 = await this.Redis.系统储存.同步自增(this.全局id)

                    let 加密密码 = 加解密.不可逆加密(账号 + 密码)
                    let 验证 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.toString(), '密码')
                    if (验证 != null) {
                        回调('账号已经被注册', null)
                        return
                    }
                    let 权限 = 系统权限.注册权限 | 系统权限.登录权限 | 系统权限.注册应用 | 系统权限.订阅权限 | 系统权限.取消订阅权限 | 系统权限.发布权限

                    let 储存 = await this.Redis.登录账号储存.同步哈希表储存多字段(账号.toString(),
                        '密码', 加解密.不可逆加密(账号 + 密码),
                        '权限', 权限.toString(), '电话', 电话,
                        '邮箱', 邮箱, '注册时间', 注册时间)
                    if (储存) {
                        let 结果: 注册结果 = {
                            账号: 账号.toString(),
                            密码: 密码,
                            电话: 电话,
                            邮箱: 邮箱,
                            注册时间: 注册时间,
                            说明: '请保管好账号及密码,本账号及密码是本站唯一凭证.'
                        }
                        回调(null, 结果)
                        return
                    } else {
                        回调('未能注册成功', null)
                        return
                    }
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('修改密码', async (数据: 修改密码, 回调: (err, 结果) => void) => {
                try {
                    if (!回调 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    if (!(数据 && 数据.原密码 && 数据.新密码)) {
                        回调('参数有错误', null)
                        return
                    }
                    let 账号 = 套接字['账号'].账号
                    let 原密码 = 数据.原密码
                    let 新密码 = 数据.新密码
                    let 加密原密码 = 加解密.不可逆加密(账号 + 原密码)
                    let 结果 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号, '密码')
                    if (结果 != 加密原密码) {
                        回调('原密码不正确,请重新输入.', null)
                        return
                    } else {
                        await this.Redis.登录账号储存.同步哈希表写入指定字段值(账号, '密码', 加解密.不可逆加密(账号 + 新密码))
                        回调(null, '成功')
                        return
                    }
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('找回密码', async (数据: 找回密码, 回调: (err, 结果) => void) => {
                try {
                    if (!回调 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    let 账号 = 套接字['账号']
                    if (数据.验证码) {
                        let 验证码 = 数据.验证码
                        let 新密码 = 数据.新密码
                        let 密码验证 = /^[\w\S]{6,10}$/g.test(新密码)
                        if (!密码验证) {
                            回调('密码不合格', null)
                            return
                        }
                        let 库内信息 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.账号, '找回密码验证码')
                        let 账号信息 = JSON.parse(库内信息) || null
                        if (账号信息) {
                            let 储存验证码 = 账号信息.验证码
                            let 申请时间 = 账号信息.时间
                            let 现在 = Date.now()
                            if ((现在 - 申请时间) > 60 * 1000) {
                                回调('验证码已过期请重新获取', null)
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '找回密码验证码')
                                return
                            }
                            if (验证码 == 储存验证码) {
                                this.Redis.登录账号储存.同步哈希表写入指定字段值(账号.账号, '密码', 加解密.不可逆加密(账号.账号 + 新密码))
                                回调(null, '密码已经重置,请妥善保管')
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '找回密码验证码')
                                return
                            } else {
                                回调('发生错误', null)
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '找回密码验证码')
                                return
                            }
                        } else {
                            回调('发生错误', null)
                            await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '找回密码验证码')
                            return
                        }
                    } else {
                        let 库记录 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.账号, '电话')
                        let 电话 = 库记录 || null
                        let 电话验证 = /^1[\d]{10}$|^null$/g.test(电话)
                        if (!电话验证) {
                            回调('没有绑定电话,请先绑定电话.', null)
                            return
                        }
                        let 验证码 = this.生成验证码()
                        await this.Redis.登录账号储存.同步哈希表写入指定字段值(账号.账号, '找回密码验证码', JSON.stringify({ 电话: 电话, 验证码: 验证码, 时间: Date.now() }))
                        let 短信参数 = JSON.stringify({ mingcheng: 账号.账号, yanzhengma: 验证码 })
                        this.短信验证器.execute('alibaba.aliqin.fc.sms.num.send', {
                            sms_type: 'normal',
                            sms_free_sign_name: this.短信配置.软件名称,
                            sms_param: 短信参数,
                            sms_template_code: this.短信配置.模板id,
                            rec_num: 电话
                        }, (err, 结果) => {
                            if (err) {
                                回调('失败', null)
                            } else {
                                回调(null, '验证码已发送,请注意查看绑定手机短信消息.')
                            }
                            return
                        })
                    }
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('修改手机', async (数据: 修改手机, 回调: (err, 结果) => void) => {
                try {
                    if (!回调 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    let 账号 = 套接字['账号']
                    if (数据.验证码) {
                        let 验证码 = 数据.验证码
                        let 新手机号 = 数据.新手机号 || null
                        let 验证新手机 = /^1[\d]{10}$/g.test(新手机号)
                        if (!验证新手机) {
                            回调('手机号码不正确', null)
                            return
                        }
                        let 库内信息 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.账号, '修改手机验证码')
                        let 账号信息 = JSON.parse(库内信息) || null
                        if (账号信息) {
                            let 储存验证码 = 账号信息.验证码
                            let 申请时间 = 账号信息.时间
                            let 现在 = Date.now()
                            if ((现在 - 申请时间) > 60 * 1000 * 2) {
                                回调('验证码已过期请重新获取', null)
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改手机验证码')
                                return
                            }
                            if (验证码 == 储存验证码) {
                                this.Redis.登录账号储存.同步哈希表写入指定字段值(账号.账号, '电话', 新手机号)
                                回调(null, '手机号码已经重新绑定,请妥善保管')
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改手机验证码')
                                return
                            } else {
                                回调('验证码不正确,请输入正确验证码', null)
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改手机验证码')
                                return
                            }
                        } else {
                            回调('发生错误', null)
                            await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改手机验证码')
                            return
                        }
                    } else {
                        let 库记录 = await this.Redis.登录账号储存.同步哈希表读多字段(账号.账号, '电话', '邮箱')
                        let 电话 = 库记录[0] || null
                        let 邮箱 = 库记录[1] || null
                        let 验证码 = this.生成验证码()
                        let 库内信息 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.账号, '修改手机验证码')
                        let 记录 = JSON.parse(库内信息) || null
                        if (记录) {
                            let 现在 = Date.now()
                            let 时差 = 现在 - 记录.时间
                            if (时差 < 60 * 1000 * 5) {
                                回调(`延时未到,请${Math.floor((60 * 1000 * 2 - 时差) / 1000)}秒后再次申请`, null)
                                return
                            }
                        }
                        await this.Redis.登录账号储存.同步哈希表写入指定字段值(账号.账号, '修改手机验证码', JSON.stringify({ 验证码: 验证码, 时间: Date.now() }))
                        let 邮件 = {
                            from: this.邮件验证参数配置.发送者,  // '372681370@qq.com', // 发送者
                            to: 邮箱,
                            subject: this.邮件验证参数配置.标题,  // '验证邮件',
                            html: this.邮件验证配置.转换变量({ 用户: 账号.账号, 验证码: 验证码 })
                        }
                        this.邮件验证器.sendMail(邮件, (err, 成功) => {
                            if (err) {
                                回调('邮件发送失败,请稍后再次发送.', null)
                            } else {
                                回调(null, '手机号码已经重新绑定,请妥善保管')
                            }
                            this.邮件验证器.close()
                            return
                        })
                    }
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('修改邮箱', async (数据: 修改邮箱, 回调: (err, 结果) => void) => {
                try {
                    if (!回调 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    let 账号 = 套接字['账号']
                    if (数据.验证码) {
                        let 验证码 = 数据.验证码
                        let 新邮箱 = 数据.新邮箱 || null
                        let 邮箱验证 = /(^[a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[\.][a-z]{2,4}([\.][a-z]{2})?$|^null$/i.test(新邮箱)
                        if (!邮箱验证) {
                            回调('邮箱输入不合法', null)
                            return
                        }
                        let 库内信息 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.账号, '修改邮箱验证码')
                        let 账号信息 = JSON.parse(库内信息) || null
                        if (账号信息) {
                            let 储存验证码 = 账号信息.验证码
                            let 申请时间 = 账号信息.时间
                            let 现在 = Date.now()
                            if ((现在 - 申请时间) > 60 * 1000) {
                                回调('验证码已过期请重新获取', null)
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改邮箱验证码')
                                return
                            }
                            if (验证码 == 储存验证码) {
                                this.Redis.登录账号储存.同步哈希表写入指定字段值(账号.账号, '邮箱', 新邮箱)
                                回调(null, '绑定邮箱已修改,请妥善保管')
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改邮箱验证码')
                                return
                            } else {
                                回调('发生错误', null)
                                await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改邮箱验证码')
                                return
                            }
                        } else {
                            回调('发生错误', null)
                            await this.Redis.登录账号储存.同步哈希表删除字段值(账号.账号, '修改邮箱验证码')
                            return
                        }
                    } else {
                        let 库记录 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.账号, '电话')
                        let 电话 = 库记录 || null
                        let 电话验证 = /^1[\d]{10}$|^null$/g.test(电话)
                        if (!电话验证) {
                            回调('没有绑定电话', null)
                            return
                        }
                        let 验证码 = this.生成验证码()
                        await this.Redis.登录账号储存.同步哈希表写入指定字段值(账号.账号, '修改邮箱验证码', JSON.stringify({ 验证码: 验证码, 时间: Date.now() }))
                        let 短信参数 = JSON.stringify({ mingcheng: 账号.账号, yanzhengma: 验证码 })
                        this.短信验证器.execute('alibaba.aliqin.fc.sms.num.send', {
                            sms_type: 'normal',
                            sms_free_sign_name: this.短信配置.软件名称,
                            sms_param: 短信参数,
                            sms_template_code: this.短信配置.模板id,
                            rec_num: 电话
                        }, (err, 结果) => {
                            回调(err, 结果)
                            return
                        })
                    }
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('注销账号', async (数据: 注销账号, 回调: (err, 结果) => void) => {
                try {
                    if (!(回调 && typeof 回调 == 'function')) {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    let 管理员账号 = 套接字['账号']
                    if (系统权限.超级管理员 != (管理员账号.权限 && 系统权限.超级管理员)) {
                        回调('权限不足', null)
                        return
                    }
                    if (!数据 && 数据.账号) {
                        回调('参数错误', null)
                        return
                    }
                    let 数量 = await this.Redis.登录账号储存.同步删除键(数据.账号)
                    if (数量 > 0) {
                        回调(null, '成功删除' + 数据.账号 + '账号.')
                        return
                    } else {
                        回调('发生错误', null)
                        return
                    }
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            //
            套接字.on('注册应用', async (数据: 注册应用<any>, 回调: (err, 结果) => void) => {
                try {
                    if (!(回调 && typeof 回调 == 'function')) {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    if (系统权限.注册应用 != (套接字['账号'].权限 & 系统权限.注册应用)) {
                        回调('权限不足', null)
                        return
                    }
                    if (!(数据 && 数据.应用名称 && (typeof 数据.应用名称 == 'string'))) {
                        回调('参数错误', null)
                        return
                    }
                    let 账号 = 套接字['账号']
                    if (!账号) {
                        回调('账号不存在', null)
                        return
                    }
                    let 库内记录 = await this.Redis.登录账号储存.同步哈希表读多字段(账号.账号, '权限', '应用数量')
                    let 所有者权限 = parseInt(库内记录[0])
                    let 应用数量 = parseInt(库内记录[1])
                    if (应用数量 > 4) {
                        回调('你的账号已经注册了5个应用,已经达到系统上限', null)
                        return
                    }
                    应用数量 = (应用数量 + 1) || 1
                    所有者权限 = 所有者权限 | 系统权限.申请应用 | 系统权限.开办业务 | 系统权限.办理业务 | 系统权限.回复结果 | 系统权限.管理员

                    let 应用id: string | number = await this.Redis.系统储存.同步自增(this.全局id)
                    应用id = 应用id.toString()
                    await this.Redis.登录账号储存.同步哈希表储存多字段(账号.账号, '应用数量', 应用数量.toString(), 应用id,
                        JSON.stringify({ '应用所有者': 账号.账号, '应用名称': 数据.应用名称, '权限': 所有者权限.toString() }))
                    let 管理员: Array<string> = 数据.管理员 || null
                    let 管理员数据 = {}
                    let 管理员密匙 = uuid()
                    if (是否为数组(管理员)) {
                        管理员.forEach((值) => {
                            let 管理员权限 = 系统权限.开办业务 | 系统权限.办理业务 | 系统权限.回复结果 | 系统权限.管理员 | 系统权限.申请应用
                            管理员数据[值] = { 应用id: 应用id, 管理员私匙: 管理员密匙, '权限': 管理员权限.toString() }
                        })
                    }
                    this.Redis.应用私匙储存.同步键值存值(管理员密匙, 应用id as string)
                    let 所有者密匙 = uuid()
                    this.Redis.应用私匙储存.同步键值存值(所有者密匙, 应用id)
                    this.Redis.应用私匙储存.同步集合插入键值(应用id, 所有者密匙, 管理员密匙)
                    管理员数据[账号.账号] = { 应用id: 应用id, 管理员私匙: 所有者密匙, '权限': 所有者权限.toString() }
                    this.Redis.应用储存.同步哈希表储存多字段(应用id, '应用所有者', 账号.账号,
                        '管理员', JSON.stringify(管理员数据), '应用名称', 数据.应用名称, '应用详情', JSON.stringify(数据.应用详情))
                    回调(null, {
                        应用id: 应用id, 应用所有者: 账号.账号, 账号私匙: 所有者密匙, 管理员: 管理员数据,
                        说明: '此账号是本应用的默认管理员,请保管好本账号的账号及密码.'
                    })
                    return
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('修改应用', async (数据: 修改应用<any>, 回调: (err, 结果) => void) => {
                try {
                    if (!(回调 && typeof 回调 == 'function')) {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    if (!(数据 && 数据.应用id && 数据.数据 && (typeof 数据.应用id == 'string') && (typeof 数据.数据 == 'object'))) {
                        回调('参数错误', null)
                        return
                    }
                    let 账号 = 套接字['账号']
                    let 应用id = 数据.应用id
                    let 结果 = await this.Redis.应用储存.同步哈希表获取指定字段值(应用id, '应用所有者')
                    if (账号.账号 != 结果) {
                        回调('无权收回', null)
                        return
                    }
                    let 修改的内容 = 数据.数据
                    for (let 项目 in 修改的内容) {
                        this.Redis.应用储存.同步哈希表写入指定字段值(应用id, 项目, 修改的内容[项目])
                    }
                    回调(null, '成功')
                    return
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('申请私匙', async (数据: 申请私匙, 回调: (err, 结果) => void) => {
                try {
                    if (!(回调 && typeof 回调 == 'function')) {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    if (!(数据 && 数据.私匙 && 数据.申请数量 && (typeof 数据.申请数量 == 'number') && (typeof 数据.私匙 == 'string'))) {
                        回调('参数错误.', null)
                        return
                    }
                    if (数据.申请数量 > 5) {
                        回调('一次最多只能申请5个.', null)
                        return
                    }
                    let 应用id = await this.Redis.应用私匙储存.同步键值读值(数据.私匙)
                    let 存在 = await this.Redis.应用储存.同步检查键(应用id)
                    if (存在 != 1) {
                        回调('应用不存在或应用id错误.', null)
                        return
                    }
                    let 应用管理员 = await this.Redis.应用储存.同步哈希表获取指定字段值(应用id, '管理员')
                    let 管理员 = JSON.parse(应用管理员) || null
                    if (!管理员) {
                        回调('发生错误,请核对好参数再试.', null)
                        return
                    }
                    let 账号 = 套接字['账号']

                    if (系统权限.申请应用 != (管理员[账号.账号].权限 & 系统权限.申请应用)) {
                        回调('权限不足.', null)
                        return
                    }
                    let 返回 = await this.Redis.应用私匙储存.同步集合读取全部成员(应用id)
                    if (返回.length >= 5) {
                        回调('一个应用只能申请5个私匙,次应用已经全部申请完了.', null)
                        return
                    }
                    for (let i = 返回.length; i < 数据.申请数量; i++) {
                        let 密 = uuid()
                        await this.Redis.应用私匙储存.同步键值存值(密, 应用id)
                        await this.Redis.应用私匙储存.同步集合插入键值(应用id, 密)
                        返回.push(密)
                    }
                    回调(null, 返回)
                    return
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('查看私匙', async (数据, 回调: (err, 结果) => void) => {
                try {
                    if (!(回调 && typeof 回调 == 'function')) {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    if (!(数据 && 数据.应用id)) {
                        回调('参数错误.', null)
                        return
                    }
                    let 账号 = 套接字['账号']
                    let 应用id = 数据.应用id
                    let 权限 = await this.Redis.登录账号储存.同步哈希表获取指定字段值(账号.账号, 应用id)
                    if (!权限) {
                        回调('账号不是应用所有者,查询私匙只能使用应用所有者账号查询', null)
                        return
                    }
                    let 返回 = await this.Redis.应用私匙储存.同步集合读取全部成员(应用id)
                    回调(null, 返回)
                    return
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('收回私匙', async (数据: 收回私匙, 回调: (err, 结果) => void) => {
                try {
                    if (!(回调 && typeof 回调 == 'function')) {
                        套接字.emit('error', '参数不正确必须有 回调:(err,结果)=>void ')
                        return
                    }
                    if (!(数据 || 数据.私匙 || 数据.应用id)) {
                        回调('参数错误.', null)
                        return
                    }
                    let 账号 = 套接字['账号']
                    let 私匙: Array<string> = 数据.私匙
                    let 应用id = 数据.应用id
                    let 结果 = await this.Redis.应用储存.同步哈希表读多字段(应用id, '应用所有者', '管理员')
                    if (账号.账号 != 结果[0]) {
                        回调('无权收回', null)
                        return
                    }
                    let 管理员 = JSON.parse(结果[1][账号.账号])
                    let 管理员私匙 = 管理员.账号私匙
                    私匙.forEach((值) => {
                        if (管理员私匙 == 值) {
                            回调('所有者私匙不能收回', null)
                            return
                        }
                        this.Redis.应用私匙储存.同步键值删除键(值)
                        this.Redis.应用私匙储存.同步集合删除成员(应用id.toString(), 值)
                    })
                    回调(null, '成功')
                    return
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            //
            套接字.on('订阅', (频道: string, 回调: (err, 结果) => void) => {
                try {
                    if (!(频道 && 回调)) {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (typeof 频道 != 'string' || 频道.length > 16 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (系统权限.订阅权限 != (套接字['账号']['权限'] & 系统权限.订阅权限)) {
                        回调('权限不足.', null)
                        return
                    }
                    套接字.join(频道)
                    this.订阅.subscribe(频道)
                    回调(null, '成功')
                    return
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('模糊订阅', (频道: string, 回调: (err, 结果) => void) => {
                try {
                    if (!(频道 && 回调)) {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (typeof 频道 != 'string' || 频道.length > 16 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (系统权限.订阅权限 != (套接字['账号']['权限'] & 系统权限.订阅权限)) {
                        回调('权限不足.', null)
                        return
                    }
                    套接字.join(频道)
                    this.订阅.psubscribe(频道)
                    回调(null, '成功')
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('取消订阅', (频道: string, 回调: (err, 结果) => void) => {
                try {
                    if (!(频道 && 回调)) {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (typeof 频道 != 'string' || 频道.length > 16 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (系统权限.取消订阅权限 != (套接字['账号']['权限'] & 系统权限.取消订阅权限)) {
                        回调('权限不足.', null)
                        return
                    }
                    套接字.leave(频道)
                    this.订阅.unsubscribe(频道)
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('取消模糊订阅', (频道: string, 回调: (err, 结果) => void) => {
                try {
                    if (!(频道 && 回调)) {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (typeof 频道 != 'string' || 频道.length > 16 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (系统权限.取消订阅权限 != (套接字['账号']['权限'] & 系统权限.取消订阅权限)) {
                        回调('权限不足.', null)
                        return
                    }
                    套接字.leave(频道)
                    this.订阅.punsubscribe(频道)
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            套接字.on('发布', (频道: string, 消息: any, 回调: (err, 结果) => void) => {
                try {
                    if (!(频道 && 消息 && 回调)) {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (typeof 频道 != 'string' || 频道.length > 16 || typeof 回调 != 'function') {
                        套接字.emit('error', '参数不正确.')
                        return
                    }
                    if (系统权限.发布权限 != (套接字['账号']['权限'] & 系统权限.发布权限)) {
                        回调('权限不足.', null)
                        return
                    }
                    let 消息体 = (typeof 消息 == 'string') ? 消息 : JSON.stringify(消息)
                    if (消息体.length > 512) {
                        回调('消息体大小超过限制.', null)
                        return
                    }
                    this.发布.publish(频道, 消息体)
                } catch (err) {
                    套接字.emit('error', err)
                    return
                }
            })
            //
            套接字.on('开办业务', async (数据: 开办业务<any>, 回调?: (err, 结果) => void) => {
                try {
                    let 账号 = 套接字['账号']
                    if (!(数据 && 数据.私匙 && 数据.业务 && 数据.数据)) {
                        回调('数据有错误', null)
                        return
                    }
                    let 应用id = await this.Redis.应用私匙储存.同步键值读值(数据.私匙)
                    if (!应用id) {
                        回调('应用不存在,请核查私匙.', null)
                        return
                    }
                    let 管理员: any = await this.Redis.应用储存.同步哈希表获取指定字段值(应用id, '管理员')
                    管理员 = JSON.parse(管理员)
                    if (系统权限.开办业务 != (管理员[账号.账号].权限 & 系统权限.开办业务)) {
                        回调 ? 回调('权限不足', null) : 套接字.emit('error', '权限不足')
                        return
                    }
                    let 验证 = /\||#/g.test(数据.业务)
                    if (验证) {
                        回调('业务名不合法不能包含字符"|"及"#"。', null)
                        return
                    }
                    if (!套接字['账号']['办理者']) {
                        套接字['账号']['办理者'] = ''
                    }
                    let MD5 = 加解密.不可逆加密(JSON.stringify(数据.数据))
                    let 结果 = await this.开办业务数据库脚本(应用id, 数据.业务, 账号.账号, MD5, JSON.stringify(数据.数据))
                    if (结果 !== 'OK') {
                        回调('业务数据MD5验证失败,请确保同名业务,的代码一致.', null)
                        return
                    }
                    套接字['账号']['办理者'] += 应用id + '|' + 数据.业务 + '#'
                    套接字['账号']['业务权限'] = 管理员[账号.账号].权限 | 系统权限.回复结果
                    回调(null, '成功')
                    return
                } catch (err) {
                    回调 ? 回调('失败', null) : 套接字.emit('error', '失败')
                }
            })
            套接字.on('办理业务', async (数据: 办理业务<any>, 回调?: (err, 结果) => void) => {
                try {
                    if (系统权限.办理业务 != (套接字['账号'].权限 & 系统权限.办理业务)) {
                        回调 ? 回调('权限不足', null) : 套接字.emit('error', '权限不足')
                        return
                    }
                    if (!(数据 && 数据.私匙 && 数据.业务)) {
                        回调 ? 回调('数据有错误', null) : 套接字.emit('error', '数据有错误')
                        return
                    }
                    let 账号 = 套接字['账号']
                    let 应用id = await this.Redis.应用私匙储存.同步键值读值(数据.私匙)
                    let 文本: string = await this.Redis.业务储存.同步键值读值(应用id + '_' + 数据.业务)
                    let 业务办理者数组: Array<string> = 文本.replace(new RegExp('^\\|?', 'g'), '').split('|')
                    let 业务办理员数量 = 业务办理者数组.length || 0
                    if (业务办理员数量 == 0) {
                        回调 ? 回调('没有这个业务', null) : 套接字.emit('error', '没有这个业务')
                        return
                    }
                    let 余数 = parseInt(套接字.id.slice(0, 8), 16) % 业务办理员数量
                    let 业务办理者 = 业务办理者数组[余数]
                    if (!业务办理者) {
                        回调 ? 回调('这个业务没有办理者', null) : 套接字.emit('error', '这个业务没有办理者')
                        return
                    }
                    let 时间 = Date.now()
                    let 消息id = uuid()
                    let 自定义事件 = 数据.自定义事件 || null

                    let 合并数据 = { id: 消息id, 自定义事件: 自定义事件, 数据: 数据.数据, 申请账号: 账号.账号, 事件: 数据.业务, 接收时间: 时间 }
                    let 办理业务的数据 = JSON.stringify(合并数据)
                    if (!办理业务的数据) {
                        回调 ? 回调('发送的数据包含错误', null) : 套接字.emit('error', '发送的数据包含错误')
                        return
                    }
                    this.Redis.消息接收储存.set(消息id, 办理业务的数据)
                    this.Redis.消息接收储存.expire(消息id, 60 * 1000 * 24)
                    this.发布.publish(业务办理者, 办理业务的数据, (err) => {
                        if (err) {
                            回调 ? 回调(JSON.stringify(err), null) : 套接字.emit('error', err)
                            return
                        } else {
                            回调 ? 回调(null, 消息id) : null
                            return
                        }
                    })
                } catch (err) {
                    回调 ? 回调(JSON.stringify(err), null) : 套接字.emit('error', err)
                    this.Redis.办理业务错误消息储存.zadd(套接字['账号'].账号, JSON.stringify(数据))
                    return
                }
            })
            套接字.on('回复结果', (数据: 回复结果<any, any>, 回调?: (err, 结果) => void) => {
                try {
                    if (系统权限.回复结果 != (套接字['账号'].业务权限 & 系统权限.回复结果)) {
                        回调 ? 回调('权限不足', null) : 套接字.emit('error', '权限不足')
                        return
                    }
                    let 消息id = 数据.申请数据['id']
                    if (!消息id) {
                        回调 ? 回调('消息id丢失', null) : null
                        return
                    }
                    let 回复频道数组 = []
                    if (是否为数组(数据.回复频道)) {
                        回复频道数组 = 数据.回复频道 as string[]
                    } else {
                        回复频道数组 = []
                        回复频道数组.push(数据.回复频道 as string)
                    }
                    let 时间 = Date.now()
                    let 自定义事件 = 数据.申请数据['自定义事件'] || null
                    let 接收时间 = 数据.申请数据['接收时间']
                    let 返回数据 = 自定义事件 ?
                        { id: 消息id, 回复数据: 数据.回复数据, 事件: 自定义事件, 接收时间: 接收时间, 回复时间: 时间 } :
                        { id: 消息id, 回复数据: 数据.回复数据, 接收时间: 接收时间, 回复时间: 时间 }
                    let 返回 = JSON.stringify(返回数据)
                    this.Redis.消息回复储存.set(消息id, 返回)
                    this.Redis.消息回复储存.expire(消息id, 60 * 1000 * 24)
                    回复频道数组.map((频道, 索引) => {
                        this.发布.publish(频道, 返回, (err, 结果) => {
                            if (err) {
                                回调 ? 回调({ ...err, 下标: 索引 }, null) : null
                                return
                            } else {
                                回调 ? 回调(null, 频道) : null
                                return
                            }
                        })
                    })
                } catch (err) {
                    回调 ? 回调(err, null) : 套接字.emit('error', err)
                    this.Redis.回复结果错误消息储存.zadd(套接字['账号'].账号, JSON.stringify(数据))
                    return
                }
            })
            // 退出服务
            套接字.on('disconnect', () => {
                if (套接字['账号']['办理者']) {
                    this.安全关闭业务(套接字)
                }
                this.登录.退出(套接字)
            })
        })
        console.log('服务器已启动!')
    }
    private 安全关闭业务 = async (套接字: SocketIO.Socket) => {

        let 账号 = 套接字['账号']
        let 业务 = 账号['办理者'] as string
        业务 = 业务.replace(/^#+|#+$/g, '')
        let 分割 = 业务.split('#')
        for (let i = 0; i < 分割.length; i++) {
            let 应用 = 分割[i].split('|')
            let 应用id = 应用[0]
            let 业务 = 应用[1]
            await this.安全退出数据库脚本(应用id, 业务, 账号)
        }
    }
    private async 开办业务数据库脚本(应用id, 业务, 账号, MD5, 数据) {
        return new Promise<any>((成功, 失败) => {
            try {
                this.Redis.业务储存.defineCommand('开办业务脚本', {
                    numberOfKeys: 1,
                    lua:
                           `local id =KEYS[1]
                            local yewu =ARGV[1]
                            local yonghu =ARGV[2]
                            local md5 =ARGV[3]
                            local shuju =ARGV[4]
                            local shifoucunzai = redis.call("get",id.."_"..yewu.."_".."MD5")
                                if(not(shifoucunzai))
                                      then
                                         redis.call("mset",id.."_"..yewu,"|"..yonghu,id.."_"..yewu.."_".."SHJ",shuju,id.."_"..yewu.."_".."MD5",md5)
                                         return "OK"
                                      end
                                if(shifoucunzai==md5)
                                      then
                                         local fanhui = redis.call("append",id.."_"..yewu,"|"..yonghu)
                                          if(fanhui>0)
                                                then
                                                     return "OK"
                                                else
                                                     return "ERR"
                                                end
                                     else
                                          return "ERR-MD5"
                                     end`
                })
                this.Redis.业务储存['开办业务脚本'](应用id, 业务, 账号, MD5, 数据, (err, 结果) => {
                    if (err)
                        失败(err)
                    成功(结果)
                })
            } catch (err) {
                失败(err)
            }
        })
    }
    private async 安全退出数据库脚本(应用id, 业务, 账号) {
        return new Promise<any>((成功, 失败) => {
            try {
                this.Redis.业务储存.defineCommand('安全退出数据库脚本', {
                    numberOfKeys: 1, lua:
                    `local id = KEYS[1] 
                     local yewu = ARGV[1]
                     local yonghu  = ARGV[2] 
                     local liebiao = redis.call("get",id.."_".. yewu)
                     local fengeyonghu="|?"..yonghu 
                     local fanhui = string.gsub(liebiao,fengeyonghu,"")
                       if(fanhui=="") 
                         then
                            redis.call("del",id.."_"..yewu.."_".."SHJ",id.."_"..yewu,id.."_"..yewu.."_".."MD5")
                           return "OK"
                          else 
                           redis.call("set" , id.."_"..yewu,fanhui)
                           return "OK"
                        end
                         return "ERR"`
                })
                this.Redis.业务储存['安全退出数据库脚本'](应用id, 业务, 账号.账号, (err, 结果) => {
                    if (err)
                        失败(err)
                    成功(结果)
                })
            } catch (err) {
                失败(err)
            }
        })
    }
    private 注册数据验证(数据) {
        let 验证数据 = 数据 || {}
        let 电话 = 验证数据.电话 || 'null'
        let 邮箱 = 验证数据.邮箱 || 'null'
        let 密码 = 验证数据.密码 || null
        if (!密码) return false
        let 电话验证 = /^1[\d]{10}$|^null$/g.test(电话)
        let 邮箱验证 = /(^[a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[\.][a-z]{2,4}([\.][a-z]{2})?$|^null$/i.test(邮箱)
        let 密码验证 = /^[\w\S]{6,10}$/g.test(密码)
        return 密码验证 && 邮箱验证 && 电话验证
    }
    private 消息循环() {
        this.订阅.on('message', (频道, 数据) => {
            this.消息处理(频道, 数据)
        })
        this.订阅.on('pmessage', (模式, 频道, 消息) => {
            this.模糊消息(模式, 频道, 消息)
        })
    }
    private 模糊消息(模式, 频道, 消息) {
        let 发布 = this.名称空间.in(模式)
        发布.emit('匹配消息', 频道, 消息)
    }
    private 消息处理(频道, 数据) {
        try {
            if (this.登录.本机登录记录[频道]) {
                let 发送 = this.名称空间.to(频道)
                let 返回 = JSON.parse(数据)
                let 消息id = 返回.id
                if (!发送 || !返回 || !消息id)
                    return
                let 事件 = 频道
                let 自定义事件 = 返回['事件'] ? 返回['事件'] : null
                if (自定义事件 == null) {
                    发送.emit(事件, 返回)
                } else {
                    delete 返回['事件']
                    发送.emit(自定义事件, 返回)
                }
                return
            } else if (this.登录.等待第三方登录[频道]) {

                let 套接字 = this.登录.等待第三方登录[频道]
                let 返回 = JSON.parse(数据)
                let 账号 = 套接字['账号']
                //登录错误提前返回
                let 自定义事件 = 返回['事件'] || '完成登录'

                if (!(返回 && 返回.回复数据 && 返回.回复数据.令牌)) {
                    套接字.emit('error', '登录错误')
                    return
                }
                this.订阅.unsubscribe(账号.账号)
                账号.账号 = 返回.回复数据.令牌
                账号.已登录 = '第三方登录'
                账号.应用 ? this.Redis.第三方登录.同步集合插入键值(账号.应用, 账号.账号) : null
                账号.权限 = 系统权限.注册权限 | 系统权限.登录权限 | 系统权限.注册应用 | 系统权限.订阅权限 | 系统权限.取消订阅权限 | 系统权限.发布权限 | 系统权限.办理业务
                套接字['账号'] = 账号
                套接字.leaveAll()
                套接字.join(账号.账号)
                this.订阅.subscribe(账号.账号)
                delete this.登录.等待第三方登录[频道]
                this.登录.本机登录记录[账号.账号] = 账号.账号
                delete 返回['事件']
                if (返回.回复数据.令牌) {
                    套接字.emit(自定义事件, null, '成功')
                } else {
                    套接字.emit(自定义事件, '失败', null)
                }
                return
            } else if (频道 == this.全局id || 频道 == this.本服id) {
                this.名称空间.emit('系统消息', 数据)
            } else {
                let 发布 = this.名称空间.in(频道)
                发布.emit('消息', 数据)
            }
        } catch (err) {
            console.log('数据有错误: ', err)
            return
        }
    }
    private 生成验证码() {
        return (100000 + Math.floor(Math.random() * (999999 - 100000))).toString()
    }
}
export type 验证 = (套接字: SocketIO.Socket, 错误处理: (err?: any) => void) => void

class 邮件模板 {
    private 模板: string
    private 变量 = {}
    constructor(模板: string) {
        this.模板 = 模板 || ''
    }
    转换变量(变量对象: Object) {
        let 变量 = 变量对象 || {}
        let 返回 = ''
        for (let k in 变量) {
            let 表达式 = '%' + k + '%'
            let 变量值 = 变量[k].toString()
            this.模板 = this.模板.replace(new RegExp(表达式, 'g'), 变量值)
        }
        return this.模板
    }
}
